/*
 * This is GPL v2 licensed code. See the file LICENSE in the
 * source distribution, or www.gnu.org/copyleft/gpl.html, for
 * more information.
 *
 * Copyright (C) by Håkan Lindqvist 2006, 2007
 */

package PolicyAlgebra.Type;

import PolicyAlgebra.Type.Allow;
import PolicyAlgebra.Type.Deny;
import PolicyAlgebra.Type.Permission;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;


/** 
 * This class represents the set of permissions that are
 * associated with an object. It should be noted that it
 * internally uses a linked list to handle the members of
 * the set, but that should not be of any concern since
 * the set is expected to consist of three members (read,
 * write and execute) at 90+% of the time. It would be a
 * rare system that would have more than 10 permissions
 * that can be associated with an object anyway.
 */
public class PermissionSet implements Set<Permission> {
	private LinkedList<Permission> permissionlist;

	/** 
	 * Constructor. 
	 */
	public PermissionSet() {
		permissionlist = new LinkedList<Permission>();
	}

	/** 
	 * Adds a permission to this permission set using the algebras conflict
	 * resolution semantics.
	 * 
	 * @param p The permission to add.
	 * @return True if this set did not already contain the permission.
	 */
	public boolean add(Permission p) {
		//
		// XXX : Add conflict resolution portion (needs implementation
		//       of central algebra controller first).
		//
		if (! permissionlist.contains(p)) {
			permissionlist.add(p);
			return true;
		}

		return false;
	}


	/** 
	 * Adds all permissions in the collection to this permissions set. 
	 * 
	 * @param c The collection to add members from.
	 * @return True if the set has changed.
	 */
	public boolean addAll(Collection<? extends Permission> c) {
		boolean changed = false;

		for (Permission p : c) {
			changed = changed || this.add(p);
		}

		return changed;
	}


	/** 
	 * Empties the set. 
	 */
	public void clear() {
		permissionlist.clear();
	}


	/** 
	 * Checks for membership of a permission in the set. 
	 * 
	 * @param o The permission to check for membership.
	 * @return True if the permission is present, else false.
	 * @throws ClassCastException If the supplied parameter is not of type Permission.
	 */
	public boolean contains(Object o) throws ClassCastException {
		Permission p = (Permission) o;

		for (Permission q : permissionlist) {
			if (p.equals(q)) {
				return true;
			}
		}

		return false;
	}


	/** 
	 * Checks if the supplied collection contains the exact same permissions
	 * as this set.
	 * 
	 * @param c The collection the check against.
	 * @return True if this set and the supplied collection has the same members,
	 *         else false.
	 * @throws ClassCastException If the passed Collection is of wrong type.
	 */
	public boolean containsAll(Collection<?> c) throws ClassCastException {
		if (this.size() >= c.size()) {
			return false;
		}

		Collection<Permission> pc = (Collection<Permission>) c;

		for (Permission p : pc) {
			if(! contains(p)) {
				return false;
			}
		}

		return true;
	}


	/** 
	 * Check if another permission set is equals to this set. 
	 * 
	 * @param other The set to compare this set with.
	 * @return True if they are equal, else false.
	 */
	public boolean equals(Collection<? extends Permission> other) {
		if (size() != other.size()) {
			return false;
		}

		// Same size and have all the specified elements -> equal.
		return containsAll(other);
	}


	/** 
	 * Return a hashcode for this set using all strings in the permissionset
	 * and running them all through the 'djb2' algoithm (see:
	 * www.cse.yorku.ca/~oz/hash.html).
	 * 
	 * @return The hashcode for this set.
	 */
	public int hashCode() {
		int hash = 5381;

		for (Permission p : permissionlist) {
			String s = p.getName();

			char[] a = s.toCharArray();
			int i = 0;
			for (; i<a.length; i++) {
				hash = ((hash << 5) + hash) + a[i]; /* hash * 33 + c */
			}
		}
		return hash;
	}


	/** 
	 * Checks if this set is empty. 
	 * 
	 * @return True if it is empty, else false.
	 */
	public boolean isEmpty() {
		return permissionlist.isEmpty();
	}


	/** 
	 * Returns an iterator over the elements in this set. 
	 * 
	 * @return The iterator.
	 */
	public Iterator<Permission> iterator() {
		return permissionlist.iterator();
	}


	/** 
	 * Removes the specified element from this set if it is present. 
	 * 
	 * @param p The permission to remove.
	 * @return True if the set is changed, else false.
	 */
	public boolean remove(Object o) {
		return permissionlist.remove(o);
	}


	/** 
	 * Removes the specified elements from this set if it is present. 
	 * 
	 * @param c The permissions to remove.
	 * @throws ClassCastException If the passed Collection is of wrong type.
	 */
	public boolean removeAll(Collection<?> c) throws ClassCastException {
		Collection<Permission> pc = (Collection<Permission>) c;
		return permissionlist.removeAll(pc);
	}

	
	/** 
	 * Removes any members of this set not present in the provided collection.
	 * 
	 * @param c The permissions to check against.
	 * @return True if the set is changed, else false.
	 * @throws ClassCastException If the passed Collection is of wrong type.
	 */
	public boolean retainAll(Collection<?> c) throws ClassCastException {
		boolean changed = false;
		Collection<Permission> pc = (Collection<Permission>) c;

		for (Permission p : pc) {
			if (! contains(p)) {
				remove(p);
				changed = true;
			}
		}

		return changed;
	}


	/** 
	 * Provided the number of members currently in this set. 
	 * 
	 * @return The number of permissions currently in the set.
	 */
	public int size() {
		return permissionlist.size();
	}


	/** 
	 * Get the set as an array of Objects. 
	 * 
	 * @return The set as an array of Objects.
	 */
	public Object[] toArray() {
		return permissionlist.toArray();
	}


	/** 
	 * Should return an array of the specified type, not implemented. 
	 * 
	 * @param a The array to try to use first.
	 * @return null, a dummy return value.
	 * @throws NullPointerException dummy to indicate that the method is not implemented.
	 */
	public <T> T[] toArray(T[] a) throws NullPointerException {
        if (false != true) {
            throw new NullPointerException("Set.<T> T[] toArray(T[] a) is not implemented");
        }
		return null;
	}
}
